package test.lib.redis

import cn.turboinfo.dongying.api.provider.autoconfigure.CommonProviderAutoConfiguration
import cn.turboinfo.dongying.api.provider.autoconfigure.SchedulerProviderAutoConfiguration
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import mu.KotlinLogging
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.Bean
import org.springframework.data.redis.connection.Message
import org.springframework.data.redis.connection.MessageListener
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.core.StringRedisTemplate
import org.springframework.data.redis.listener.ChannelTopic
import org.springframework.data.redis.listener.RedisMessageListenerContainer
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.StringRedisSerializer
import org.springframework.test.context.TestPropertySource

@SpringBootTest(classes = [RedisMessageListenerTestApplication::class])
@TestPropertySource("classpath:test-redis.properties")
class RedisMessageListenerTest {

    private val logger = KotlinLogging.logger {}

    @Autowired
    private lateinit var stringRedisTemplate: StringRedisTemplate

    @Autowired
    private lateinit var redisTemplate: RedisTemplate<String, Any>

    @Test
    fun testStringMessage() {
        // 循环发送消息
        for (i in 1..10) {
            stringRedisTemplate.convertAndSend("test-topic", "hello world! $i")
        }
        // 等待消息消费
        Thread.sleep(5000)
    }

    @Test
    fun testObjectMessage() {
        // 循环发送消息
        for (i in 1..10) {
            redisTemplate.convertAndSend("test-object", mapOf("name" to "xiaoman", "age" to 18))
        }
        // 等待消息消费
        Thread.sleep(5000)
    }
}

@SpringBootApplication(exclude = [cn.turboinfo.dongying.api.provider.autoconfigure.CommonProviderAutoConfiguration::class, cn.turboinfo.dongying.api.provider.autoconfigure.SchedulerProviderAutoConfiguration::class])
class RedisMessageListenerTestApplication {

    @Bean
    @ConditionalOnMissingBean
    protected fun objectMapper(): ObjectMapper {
        return ObjectMapper()
    }

    @Bean
    protected fun redisTemplate(
        redisConnectionFactory: RedisConnectionFactory,
        objectMapper: ObjectMapper
    ): RedisTemplate<String, Any> {
        val redisTemplate = RedisTemplate<String, Any>()
        redisTemplate.connectionFactory = redisConnectionFactory

        // key 和 hashKey 序列化
        val keySerializer = StringRedisSerializer()
        redisTemplate.keySerializer = keySerializer
        redisTemplate.hashKeySerializer = keySerializer

        // value hashValue值的序列化
        val valueSerializer = Jackson2JsonRedisSerializer(Any::class.java)
        valueSerializer.setObjectMapper(objectMapper)
        redisTemplate.valueSerializer = valueSerializer
        redisTemplate.hashValueSerializer = valueSerializer

        redisTemplate.afterPropertiesSet()

        return redisTemplate
    }

    @Bean
    protected fun test1ListenerContainer(redisConnectionFactory: RedisConnectionFactory): RedisMessageListenerContainer {
        val container = RedisMessageListenerContainer()
        container.connectionFactory = redisConnectionFactory
        container.addMessageListener(Test1Listener(), ChannelTopic("test-topic"))
        return container
    }

    @Bean
    protected fun test2ListenerContainer(redisConnectionFactory: RedisConnectionFactory): RedisMessageListenerContainer {
        val container = RedisMessageListenerContainer()
        container.connectionFactory = redisConnectionFactory
        container.addMessageListener(Test2Listener(), ChannelTopic("test-topic"))
        return container
    }

    @Bean
    protected fun testObjectListenerContainer(
        redisConnectionFactory: RedisConnectionFactory,
        objectMapper: ObjectMapper
    ): RedisMessageListenerContainer {
        val container = RedisMessageListenerContainer()
        container.connectionFactory = redisConnectionFactory
        container.addMessageListener(TestObjectListener(objectMapper), ChannelTopic("test-object"))
        return container
    }

    fun main(args: Array<String>) {
        SpringApplication.run(RedisMessageListenerTestApplication::class.java, *args)
    }

}

class Test1Listener : MessageListener {

    override fun onMessage(message: Message, pattern: ByteArray?) {
        pattern
            ?.also {
                val topic = String(it)
                val context = String(message.body)
                println("Test1Listener Message received: topic=$topic, message=$context")
            }
    }

}

class Test2Listener : MessageListener {

    override fun onMessage(message: Message, pattern: ByteArray?) {
        pattern
            ?.also {
                val topic = String(it)
                val context = String(message.body)
                println("Test2Listener Message received: topic=$topic, message=$context")
            }
    }

}

class TestObjectListener(
    private val objectMapper: ObjectMapper,
) : MessageListener {

    override fun onMessage(message: Message, pattern: ByteArray?) {
        pattern
            ?.also {
                val topic = String(it)
                val context = String(message.body)

                val typeRef = object : TypeReference<HashMap<String, Any>>() {}
                val map = objectMapper.readValue(context, typeRef)

                println("Test2Listener Message received: topic=$topic, message=$map")
            }
    }

}
